/*
 * ProcessHandle.cpp
 *
 *  Created on: Dec 9, 2012
 *      Author: omnia
 */

#include			"global.h"
#include			"syscalls.h"
#include			"protos.h"
#include			"string.h"
#include "Definitions.h"

/**
 * GET PROCESS ID
 *
 * 	This routine checks the Ready Queue for a name that
 *	matches the name passed to this routine. If the input
 *	to the routine is "", then the currently running process'
 *	ID is returned. Success or fail is passed to the OS, and
 *	also -1 is returned from the function if the name does
 *	not exist.
 */
void doLock() {

	INT32 LockResult;
	ZCALL(Z502_READ_MODIFY( MEMORY_INTERLOCK_BASE + 1, 1,true, &LockResult ));
}
void unlock() {
	INT32 LockResult;
	ZCALL(Z502_READ_MODIFY( MEMORY_INTERLOCK_BASE + 1, 0,true, &LockResult ));
}
INT32 get_PCB_ID(PCB ** ptrFirst, char *name, INT32 *process_ID, INT32 *error) {

	doLock();
	if (strcmp("", name)==0) {

		(*process_ID) = current_PCB->processId;
		(*error) = ERR_SUCCESS;
		unlock();

		return 0;
	}
	PCB *currptr = *ptrFirst;
	//loop on the list
	while (currptr != NULL) {
		if (strcmp(currptr->processName, name) == 0) {
			(*process_ID) = currptr->processId;
			(*error) = ERR_SUCCESS;
			unlock();
			return 0;
		}
		currptr = (PCB*) currptr->next;
	}
	(*error) = ERR_BAD_PARAM;
	unlock();
	return -1;
}
void terminate_Process(INT32 process_ID, INT32 *error) {
	INT32 status;
	PCB *toSwitch;
	//printf("PID-----------------------%ld",process_ID);
	if (total_pid <= 0)
		ZCALL( Z502_HALT());
	if (process_ID == -1) {
		//terminate itself
		CALL(status = rm_from_readyQueue(current_PCB->processId));
		if (status) {
			//it exists
			(*error) = ERR_SUCCESS;
		} else {
			//it doesn't exist
			(*error) = ERR_BAD_PARAM;

		}
		if (total_pid > 0) {
			CALL(toSwitch = get_readyPCB());
			if (toSwitch == NULL) {
				ZCALL(EVENT_IDLE());
			} else {
				CALL( switch_Killcontext(toSwitch));
			}
		} else {
			//no more process in the system
			ZCALL( Z502_HALT());

		}
	} else if (process_ID == -2) {
		//terminate itself and its children
		CALL( rm_children(&pidList, current_PCB->processId));
		CALL( status = rm_from_readyQueue(current_PCB->processId));
		CALL(status = rm_from_readyQueue(current_PCB->processId));
		if (status) {
			//it exists
			(*error) = ERR_SUCCESS;
		} else {
			//it doesn't exist
			(*error) = ERR_BAD_PARAM;

		}
		if (total_pid > 0) {
			CALL(toSwitch = get_readyPCB());
			if (toSwitch == NULL) {
				ZCALL(EVENT_IDLE());
			} else {
				CALL( switch_Killcontext(toSwitch));
			}
		} else {
			//no more process in the system
			ZCALL( Z502_HALT());

		}

	} else {
		CALL( status = rm_from_readyQueue(process_ID));
		if (status)
			(*error) = ERR_SUCCESS;
		else {

			(*error) = ERR_BAD_PARAM;
		}
		if (total_pid <= 0) {
			ZCALL( Z502_HALT());
		}
	}

}
void rm_children(PCB ** ptrFirst, INT32 process_ID) {
	PCB *currPtr = *ptrFirst;
	while (currPtr != NULL) {
		if (currPtr->parent == process_ID) {
			CALL( rm_from_readyQueue( currPtr->processId ));

		}
		currPtr = (PCB*) currPtr->next;
	}
}
//it's a helper function for suspend_Process
INT32 ready_to_Suspend(INT32 process_ID) {
	doLock();
	PCB *currPtr = pidList;
	while (currPtr != NULL) {
		if (currPtr->processId == process_ID) {
			if (currPtr->state == SUSPENDED_STATE) {
				unlock();
				//error
				return -1;
			}
			currPtr->state = SUSPENDED_STATE;
			unlock();
			return 1;
		}
		currPtr = (PCB*) currPtr->next;
	}
	unlock();
	return -1;
}
void suspend_Process(INT32 process_ID, INT32 *error) {
	PCB * switchPCB;
		INT32	status;

		INT32 check;
		CALL( check = check_pid_ID(process_ID) );
		if (process_ID == -1) check = 1;
		if (check == -1){

			(*error) = ERR_BAD_PARAM;
			return;
		}

		//if pid -1, suspend self
		if ( process_ID == -1 ){
			//RUNNING TO SUSPEND STATE
			CALL( status = ready_to_Suspend(current_PCB->processId) );
			if (status == 1) (*error) = ERR_SUCCESS;
			else (*error) = ERR_BAD_PARAM;


			//GET READY PCB TO RUN OR IDLE
			CALL( switchPCB = get_readyPCB() );
			if (switchPCB == NULL) ZCALL( EVENT_IDLE() );
			if (switchPCB != NULL) CALL( switch_Savecontext(switchPCB) );
		}
		else{
			//READY TO SUSPEND STATE
			CALL( status = ready_to_Suspend(process_ID) );
			if (status == 1) (*error) = ERR_SUCCESS;
			else (*error) = ERR_BAD_PARAM;


		}

}

void resume_Process(INT32 process_ID, INT32 *error) {
	doLock();
	PCB *currPtr = pidList;
	INT32 check;
	CALL( check = check_pid_ID(process_ID));
	if (check == -1) {
		(*error) = ERR_BAD_PARAM;
		return;
	}
	while (currPtr != NULL) {
		if (currPtr->processId == process_ID) {
			if (currPtr->state == SUSPENDED_STATE) {
				currPtr->state = READY_STATE;
				unlock();
				(*error) = ERR_SUCCESS;
				return;
			}

		}

		currPtr = (PCB*) currPtr->next;
	}
	unlock();
	(*error) = ERR_BAD_PARAM;
	return;

}
